package org.voidness.squaretilesframework.sprites;

import java.util.Vector;

import org.voidness.oje2d.Utils;

/** An animation class. */
public class GLHotSpotAnimation {
    /** The animation's frames */
    private Vector<GLHotSpotFrame> frames = null;

    /** The ammount of time between frames */
    private int updateInterval;

    /** The current frame */
    private int currentFrame;

    /** Is this animation repeating the loop forever? */
    private boolean isLoop;

    /** Time of the last frame change */
    private long lastTime;

    /** The current time */
    private long currentTime;

    /**
     * Is this animation running (i.e. changing frames every "updateInterval"
     * ms?
     */
    private boolean running;

    /**
     * Default constructor.
     */
    public GLHotSpotAnimation() {
        init();
    }

    /**
     * Dumps the animation frames information
     */
    public void printInfo() {
        for (int i = 0; i < frames.size(); i++) {
            GLHotSpotFrame frame = (GLHotSpotFrame) frames.elementAt(i);
            System.out.println("Frame " + i + ": " + frame.getHotSpot().width
                    + "," + frame.getHotSpot().height);
        }
    }

    /**
     * Reloads all frames
     */
    public void reload() {
        for (int i = 0; i < frames.size(); i++)
            ((GLHotSpotFrame) frames.elementAt(i)).reload();

        Utils.dump("Loaded animation frames: " + frames.size(),
                Utils.INFORMATION);
    }

    /**
     * Private initialization method. To be called by every constructor.
     */
    private void init() {
        frames = new Vector<GLHotSpotFrame>();
        updateInterval = 100; // default
        currentFrame = 0;
        isLoop = true; // Loop by default
        running = true;

        lastTime = System.currentTimeMillis();
        currentTime = lastTime;
    }

    /**
     * Adds a new frame to the animation
     * 
     * @param mNewFrame The new frame to add
     */
    public void addFrame(GLHotSpotFrame mNewFrame) {
        frames.add(mNewFrame);
    }

    /**
     * Removes the frame at the given index.
     * 
     * @param mIndex The index of the frame to remove
     */
    public void removeFrameAt(int mIndex) {
        if (mIndex > 0 && mIndex < frames.size())
            frames.removeElementAt(mIndex);
    }

    /**
     * (Re)Defines the update interval, i.e. the time between frames.
     * 
     * @param mMilliseconds The new value for the update interval
     */
    public void setUpdateInterval(int mMilliseconds) {
        updateInterval = mMilliseconds;
    }

    /**
     * Set's the animation at a defined frame.
     * 
     * @param mIndex The index of the frame to select
     */
    public void setCurrentFrame(int mIndex) {
        currentFrame = mIndex;
    }

    /**
     * Resets the animation.
     */
    public void reset() {
        currentFrame = 0;
        running = true;
    }

    /**
     * Updates the animation, based on the update interval
     */
    private void update() {
        currentTime = System.currentTimeMillis();
        if (currentTime - lastTime > updateInterval && running) {
            currentFrame++;
            lastTime = currentTime;

            if (currentFrame >= frames.size()) {
                currentFrame = 0;
                if (!isLoop)
                    running = false;
            }
        }
    }

    /**
     * Draws the animation using full alpha
     * 
     * @param mX The X component where to draw
     * @param mY The Y component where to draw
     */
    public void draw(int mX, int mY) {
        draw(mX, mY, 1.0f);
    }

    /**
     * Draws the animation
     * 
     * @param mX Where to draw (X coordinate)
     * @param mY Where to draw (Y coordinate)
     * @param mAlpha The alpha value to use
     */
    public void draw(int mX, int mY, float mAlpha) {
        if (!frames.isEmpty()) {
            // Over or underflow protection
            if ((currentFrame < 0 || currentFrame >= frames.size()) && running)
                currentFrame = 0;

            ((GLHotSpotFrame) frames.elementAt(currentFrame)).draw(mX, mY,
                    mAlpha);

            update(); // Self update. Outside is just messier...
        }
    }

    /**
     * Removes all frames from the animation.
     */
    public void removeAllFrames() {
        frames.removeAllElements();
        reset();
    }

    /**
     * Sets whether or not the animation will loop forever.
     * 
     * @param mLoop True if we want the animation never to stop, false otherwise
     */
    public void setAnimationLoop(boolean mLoop) {
        isLoop = mLoop;
    }

    /**
     * Gets whether or not the animation is looping forever.
     * 
     * @return True if it is, false if it's not
     */
    public boolean hasAnimationLoop() {
        return isLoop;
    }

    /**
     * Returns the number of frames in the animation
     * 
     * @return The number of frames
     */
    public int getNumberOfFrames() {
        return frames.size();
    }

    /**
     * Returns the current frame
     * 
     * @return The current frame's index
     */
    public int getCurrentFrame() {
        return currentFrame;
    }

    /**
     * Returns one animation frame
     * 
     * @param mPosition The index of the frame to get
     * @return The frame in that position
     */
    public Object getFrameAt(int mPosition) {
        return frames.elementAt(mPosition);
    }

    /**
     * Clones this animation
     * 
     * @return A new instance of this animation
     */
    public Object clone() {
        GLHotSpotAnimation animation = new GLHotSpotAnimation();

        for (int index = 0; index < frames.size(); index++) {
            GLHotSpotFrame frame = (GLHotSpotFrame) frames.elementAt(index);
            animation.addFrame((GLHotSpotFrame) frame.clone());
        }

        return animation;
    }
}